package cn.vendsystem.com.service.impl;


import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.vendcommon.com.entity.DeductionRecord;
import cn.vendcommon.com.entity.constants.CommonCodeMsg;
import cn.vendcommon.com.entity.constants.LoginLogConstants;
import cn.vendcommon.com.entity.LoginLog;
import cn.vendcommon.com.entity.User;
import cn.vendcommon.com.entity.enums.CommonRedisKey;
import cn.vendcommon.com.entity.vo.TokenVO;
import cn.vendcommon.com.entity.vo.UserVO;
import cn.vendcommon.com.exception.VendException;
import cn.vendcommon.com.utils.Md5Util;
import cn.vendcommon.com.utils.UserContext;
import cn.vendsystem.com.config.RabbitConfig;
import cn.vendsystem.com.mapper.UserMapper;
import cn.vendsystem.com.service.UserService;
import cn.vendsystem.com.utils.AliOssUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private AliOssUtil aliOssUtil;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public User findByUserId(Long userId) {
        return userMapper.findByUserId(userId);
    }

    @Override
    public User findByUserName(String username) {
        return userMapper.findByUserName(username);
    }

    @Override
    public void register(String phone, String password) {
        // 加密
        String md5String = Md5Util.getMD5String(password);
        userMapper.add(phone,md5String);
    }

    @Override
    public void update(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user);
    }

    /**
     * 更新头像
     */
    @Override
    public void updateAvatar(String avatarUrl) {
        Long userId = UserContext.getUserId();
        String oldAvatar = userMapper.getAvatar(Math.toIntExact(userId));
        if(!oldAvatar.equals(avatarUrl)){
            // 删除旧头像
            if(oldAvatar != null && !oldAvatar.isEmpty()){
                aliOssUtil.delete(oldAvatar);
            }
        }
        userMapper.updateAvatar(avatarUrl, Math.toIntExact(userId));
    }

    /**
     * 更新密码
     * @param newPwd
     */
    @Override
    public void updatePwd(String newPwd) {
        int id = Math.toIntExact(UserContext.getUserId());
        userMapper.updatePwd(Md5Util.getMD5String(newPwd),id);
    }

    /**
     * 更新用户基本信息
     * @param user
     */
    @Override
    public void updateBaseInfo(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userMapper.updateBaseInfo(user);
    }

    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }

    @Override
    public void addUser(User user) {
        // 默认密码 md5("123456")
        if (user.getPassword() == null) {
            user.setPassword(Md5Util.getMD5String("123456"));
        }
        userMapper.insert(user);
    }

    @Override
    public void updateUser(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user);
    }

    @Override
    public void deleteUser(Integer id) {
        // 删除redis
        redisTemplate.delete(CommonRedisKey.USER_TOKEN.getRealKey(id.toString()));
        // 删除用户头像
        String userPic = userMapper.findUserPicByUserId(Long.valueOf(id));
        if(userPic != null && !userPic.isEmpty()){
            aliOssUtil.delete(userPic);
        }
        userMapper.delete(id);
    }

    @Override
    public void changeStatus(Integer id, Integer status) {
        userMapper.updateStatus(id, status);
    }

    @Override
    public TokenVO login(String phone, String password, String ip, String token) {
        LoginLog loginLog = new LoginLog(phone, ip, new Date());

        User user = getByToken(token,phone,ip);
        TokenVO tokenVO;

        if(user == null) {
            // 用户未登录或 token 无效
            User userLogin = userMapper.findByPhone(phone);
            if(userLogin == null || !Md5Util.getMD5String(password).equals(userLogin.getPassword())) {
                loginLog.setState(LoginLogConstants.LOGIN_PASSWORD_ERROR);
                safeSendLoginLog(loginLog);
                throw new RuntimeException("请确认您的手机号或密码是否正确");
            }
            user = userLogin;
            user.setLoginIP(ip);
            // 更新数据表user的登录IP
            userMapper.updateLoginIP(ip,user.getId());
            token = createToken(user);
            loginLog.setState(LoginLogConstants.LOGIN_SUCCESS);
            safeSendLoginLog(loginLog);
        } else {
            // token 有效
            loginLog.setState(LoginLogConstants.LOGIN_SUCCESS);
            safeSendLoginLog(loginLog);
        }

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        tokenVO = new TokenVO(token, userVO);

        return tokenVO;
    }

    /**
     * 将 LoginLog 发到 MQ 的封装方法 — 使用 try/catch 保护，MQ 异常不会影响登录逻辑
     */
    private void safeSendLoginLog(LoginLog loginLog) {
        try {
            // 使用上面配置的交换机和 routingKey
            rabbitTemplate.convertAndSend(RabbitConfig.LOGIN_EXCHANGE, RabbitConfig.LOGIN_ROUTING_KEY, loginLog);
        } catch (Exception ex) {
            // 这里建议写入本地日志文件或降级到 DB 记录（以便补偿）
            // 下面只是示例：打印日志（实际项目请使用 logger）
            System.err.println("发送登录日志到 MQ 失败: " + ex.getMessage());
            // 可选：把 log 写到本地 DB 或文件以便后续补偿
        }
    }

    @Override
    public User findByPhone(String phone) {
        return userMapper.findByPhone(phone);
    }

    @Override
    public void logout(String token) {
        // 清除缓存中的Token令牌
        redisTemplate.delete(CommonRedisKey.USER_TOKEN.getRealKey(token));
        UserContext.removeUserId();
    }

    /**
     * 微信登录
     *
     * @param code
     * @return
     */
    @Override
    public TokenVO loginWX(String code) {
        // 1 获取code值，使用微信工具包对象，获取微信唯一标识openid
        int userId = getUserIdByCode(code);
        User user = userMapper.findByUserId((long) userId);
        if(user == null){
            throw new VendException(CommonCodeMsg.LOGIN_ERROR);
        }
        // 生成token令牌
        String token = createToken(user);

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        TokenVO tokenVO = new TokenVO(token, userVO);
        // 2 封装tokenVO对象，返回结果
        return tokenVO;

    }

    /**
     * 微信登录
     *
     * @param code
     * @return
     */
    @Override
    public TokenVO loginWXSimple(String code) {
        // 1 获取code值，使用微信工具包对象，获取微信唯一标识openid
        int userId = getUserIdByCode(code);
        User user = userMapper.findByUserId((long) userId);
        if(user == null){
            throw new VendException(CommonCodeMsg.LOGIN_ERROR);
        }
        // 生成token令牌
        String token = createToken(user);

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        TokenVO tokenVO = new TokenVO(token, userVO);
        // 2 封装tokenVO对象，返回结果
        return tokenVO;

    }

    /**
     * 扣减用户金额
     * @param userId
     * @param amount
     */
    @Override
    public void deductionUserBalance(Long userId, Double account) {
        // 1 从数据库查询用户当前金额
        User user = userMapper.findByUserId(userId);
        if(user == null){
            throw new VendException(CommonCodeMsg.USER_NOT_FOUND);
        }
        Double currentBalance = user.getAccount();
        if(currentBalance < account){
            throw new VendException(CommonCodeMsg.INSUFFICIENT_BALANCE);
        }
        // 2 计算新金额
        Double newBalance = currentBalance - account;
        // 3 更新用户金额
        user.setAccount(newBalance);
        userMapper.updateAccountById(userId,newBalance);
    }

    @Override
    public int getRoleByUserId(Long userId) {
        return userMapper.getRoleById(userId);
    }

    @Override
    public int getRoleByPhone(String phone) {
        Integer role = userMapper.getRoleByPhone(phone);
        if (role == null) {
            // 找不到用户或没有 role 时抛出业务异常
            throw new VendException(CommonCodeMsg.USER_NOT_FOUND);
        }
        return role;
    }

    /**
     * 根据code获取用户id
     * @param code
     * @return
     */
    private Integer getUserIdByCode(String code) {
        // 1 获取code值，使用微信工具包对象，获取微信唯一标识openid
        String openId = null;
        try {
            // 引入依赖的工具包封装调用：通过code换取微信会话信息
            WxMaJscode2SessionResult sessionInfo =
                    wxMaService.getUserService().getSessionInfo(code);
            openId = sessionInfo.getOpenid();
        } catch (WxErrorException e) {
            log.error("微信登录失败，code: {}", code, e);
            throw new RuntimeException(e);
        }

        // 2 根据openid查询数据库表，判断是否第一次登录
        //如果openid不存在返回null，如果存在返回一条记录
        //select * from customer_info ci where ci.wx_open_id = ''
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getWxOpenId,openId);  // 构建查询条件：wx_open_id = ?
        User user = userMapper.selectOne(wrapper);

        // 3 如果第一次登录，添加信息到用户表
        if(user == null) {
            user = new User();
            // 设置默认昵称（时间戳）
            user.setNickname(String.valueOf(System.currentTimeMillis()));
            // 设置默认头像customerInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
            user.setWxOpenId(openId);  // 绑定微信openid
            userMapper.insert(user);  // 插入新用户记录
            log.info("新用户注册，openid: {}", openId);
        }

        // 4 记录登录日志信息

        log.debug("用户登录成功，ID: {}", user.getId());

        // 5 返回用户id
        return user.getId();
    }



    private String createToken(User user) {
        String token = UUID.randomUUID().toString().replace("-", "");
        CommonRedisKey redisKey = CommonRedisKey.USER_TOKEN;
        redisTemplate.opsForValue().set(redisKey.getRealKey(token), JSON.toJSONString(user), redisKey.getExpireTime(),redisKey.getUnit());
        return token;
    }

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    private User getByToken(String token, String phone,String ip) {
        String strObj = (String) redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token));
        if(StringUtils.isEmpty(strObj)){
            return null;
        }
        User user = JSON.parseObject(strObj, User.class);
        // 判断获取的token是否为当前登录的用户，如果不是，则需再次生成token
        if(!user.getPhone().equals(phone) || !user.getLoginIP().equals(ip)){
            return null;
        }
        return user;
    }


}
